/*************************************************************************************
 * Nombre del archivo: Memoria.java
 * Universidad del Valle de Guatemala
 * Curso: Sistemas Operativos
 * @author martin
 *          Martín Guzmán 08041
 * Fecha: Mayo de 2010
 * Descripción:
*************************************************************************************/
import java.io.File;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Memoria {

    public Frame[] frames;
    public Integer[] procesos_asignados_a_frames;
    public boolean[] frames_disponibles;
    public String nombre = "Memoria";
    public int tam_frame = 0;
    public int cant_frames = 0;
    //public int frames_disponinbles = 0;

    /***********************************************
     * Método: Memoria
     * Uso: constructor
    ***********************************************/
    public Memoria(){}

    /***********************************************
     * Método: Memoria
     * Uso: constructor
    ***********************************************/
    public Memoria(int cant_frames, int tam_frame){
        this.cant_frames = cant_frames;
        this.tam_frame = tam_frame;
        //la memoria contiene un arreglo de frames de tamaño cant_frames
        frames = new Frame[cant_frames];
        frames_disponibles = new boolean[cant_frames];
        procesos_asignados_a_frames = new Integer[cant_frames];
        //cada frame contiene un tamaño de memoria de tam_frame
        for(int i=0;i<frames.length;i++){
            frames[i]= new Frame(tam_frame);
            frames_disponibles[i] = true;
            procesos_asignados_a_frames[i] = -1;
        }
    }

    /***********************************************
     * Método: Memoria
     * Uso: constructor
    ***********************************************/
    public Memoria(int cant_frames, int tam_frame, String nombre){
        this.nombre = nombre;
        this.cant_frames = cant_frames;
        this.tam_frame = tam_frame;
        //la memoria contiene un arreglo de frames de tamaño cant_frames
        frames = new Frame[cant_frames];
        frames_disponibles = new boolean[cant_frames];
        procesos_asignados_a_frames = new Integer[cant_frames];
        //cada frame contiene un tamaño de memoria de tam_frame
        for(int i=0;i<frames.length;i++){
            frames[i]= new Frame(tam_frame);
            frames_disponibles[i] = true;
            procesos_asignados_a_frames[i] = -1;
        }
    }

    /***********************************************
     * Método: setLocalidad
     * Uso: Setea el valor de una localidad de un frame
     * @return boolean si se pudo o no
    ***********************************************/
    public boolean setLocalidad(int frame, int localidad, int valor){
        try{
            boolean a = frames[frame].setLocalidad(localidad, valor);
            if(a==false){
                return false;
            }
        }
        catch(Exception exp){
            System.out.println("No se puede hacer set a esa localidad: frame #"+frame+", localidad: "+localidad);
            return false;
        }
        return true;
    }

    /***********************************************
     * Método: resetFrame
     * Uso: reinicia el frame específico
     * @return int con la cantidad de frames
    ***********************************************/
    public boolean resetFrame(int frame){
        try{
            frames[frame] = new Frame(this.tam_frame);
            frames_disponibles[frame] = true;
            procesos_asignados_a_frames[frame] = -1;
        }
        catch(Exception exp){
            System.out.println("No se puede resetear el frame porque no existe: frame #"+frame);
            return false;
        }
        return true;
    }

    /***********************************************
     * Método: asignarFrame
     * Uso: reinicia el frame específico
     * @return int con la cantidad de frames
    ***********************************************/
    public int asignarFrame(Proceso proc){
        int retorno = -1;

        retorno = getPrimerFrameDisponible();
        if(retorno!=-1){
            this.frames_disponibles[retorno] = false;
            //TODO revisar si se crean aliases
            this.procesos_asignados_a_frames[retorno] = proc.process_ID;
        }

        return retorno;
    }

    /***********************************************
     * Método: getFirstExcept
     * Uso: reinicia el frame específico
     * @return int con la cantidad de frames
    ***********************************************/
    public int getFirstExcept(Proceso proc){
        int retorno = -1;

        for(int i=0;i<this.procesos_asignados_a_frames.length;i++){
            if(this.procesos_asignados_a_frames[i]!=proc.process_ID){
                return i;
            }
        }

        return retorno;
    }

    /***********************************************
     * Método: getPrimerFrameDisponible
     * Uso: retorna el primer frame disponible de la lista de frames o -1 si no hay
     * @return int con la cantidad de frames
    ***********************************************/
    public int getPrimerFrameDisponible(){
        for(int i=0;i<this.frames_disponibles.length;i++){
            if(this.frames_disponibles[i]==true){
                return i;
            }
        }
        return -1;
    }

    /***********************************************
     * Método: getLocalidad
     * Uso: Devuelve el valor de la localidad de determinado frame
     * @return int con la cantidad de la localidad
    ***********************************************/
    public Integer getLocalidad(int frame, int localidad){

        Integer result = null;
        try{
            result = this.frames[frame].getLocalidad(localidad);
        }catch(Exception exp){
            System.out.println("No se puede hacer get a esa localidad: frame #"+frame+", localidad: "+localidad);
            return null;
        }
        return result;
    }

    /***********************************************
     * Método: getCantFrames
     * Uso: Devuelve la cantidad de frames que tiene la memoria
     * @return int con la cantidad de frames
    ***********************************************/
    public int getCantFrames(){
        return this.cant_frames;
    }

    /***********************************************
     * Método: getCantFramesDisponibles
     * Uso: Devuelve la cantidad de frames que tiene la memoria
     * @return int con la cantidad de frames
    ***********************************************/
    public int getCantFramesDisponibles(){
        int contador = 0;

        for(int i=0;i<this.frames_disponibles.length;i++){
            if(this.procesos_asignados_a_frames[i]==-1){
                contador++;
            }
        }

        return contador;
    }

    /***********************************************
     * Método: toString
     * Uso: Devuelve una cadena con el string que representa la memoria
    ***********************************************/
    public String toString(){
        String retorno = "\n*********** Memoria: "+this.nombre+" ***********\n";
        for(int i=0;i<frames.length;i++){
            String contiene = ((this.procesos_asignados_a_frames[i]!=-1)?" asignado a proceso "+this.procesos_asignados_a_frames[i]:" no asignado a ningún proceso");
            retorno+="Frame #"+i+" "+contiene+""+frames[i].toString();
        }
        retorno += "\n***************************************\n";
        retorno += "\tFrames disponibles: "+((double)this.getCantFramesDisponibles()/(double)this.getCantFrames()*100.00)+"%";
        retorno += "\n\tFrames no disponibles: "+((double)(this.getCantFrames()-this.getCantFramesDisponibles())/(double)this.getCantFrames()*100.00)+"%";
        retorno += "\n***************************************\n";
        return retorno;
    }


}
